from glob import glob
from threading import Thread
from collections import OrderedDict
import xml.etree.ElementTree as ET
import os.path as op
import dateutil.parser
import sys

import wx
from XMLtoExcelGui import XMLtoExcelWindow
from utils import FileDropTarget, RedirectText


class XML2DataFrame:
    """Ref: http://www.austintaylor.io/lxml/python/pandas/xml/dataframe/2016/07/08/convert-xml-to-pandas-dataframe/"""

    def __init__(self, xml_data):
        self.root = ET.XML(xml_data)

    def parse_root(self, root):
        """Return a list of dictionaries from the text
         and attributes of the children under this XML root."""
        return [self.parse_element(child) for child in iter(root)]

    def parse_element(self, element, parsed=None):
        """ Collect {key:attribute} and {tag:text} from thie XML
         element and all its children into a single dictionary of strings."""
        if parsed is None:
            parsed = OrderedDict()

        for key in element.keys():
            if key not in parsed:
                parsed[key] = element.attrib.get(key)
            # else:
                # raise ValueError('duplicate attribute {0} at element {1}'.format(key, element.getroottree().getpath(element)))

        """ Apply recursion"""
        for child in list(element):
            self.parse_element(child, parsed)

        return parsed

    def process_data(self):
        """ Initiate the root XML, parse it, and return a dataframe"""
        structure_data = self.parse_root(self.root)
        return pd.DataFrame(structure_data)


class XMLtoExcel():
    def __init__(self, dirPath, outPath=None):
        self.xmlFileExt = '.xml'
        self.excelExt = '.xlsx'
        self.dirPath = dirPath
        if outPath is None:
            self.outPath = dirPath
        else:
            self.outPath = outPath

        self.xmlFiles = sorted(glob(op.join(self.dirPath, '*' + self.xmlFileExt)))
        self.dirbn = op.basename(self.dirPath)

    def convertXMLtoDataframe(self, xmlFileName, excelPostfix='.xlsx',
                              inEncoding='utf16', outEncoding=None, columnNames=None):
        if outEncoding is None:
            outEncoding = inEncoding
        xmlFileBytes = open(xmlFileName, encoding=inEncoding).read().encode(inEncoding)
        xml2df = XML2DataFrame(xmlFileBytes)
        xmlDataframe = xml2df.process_data()
        return xmlDataframe

    def convertAllXMLtoExcel(self):
        print(f'[INFO] ===== Manipulating <{self.dirbn}> =====')
        print(f'[INFO] Converting XML files to Excels ...')
        dfs = []
        for i, f in enumerate(self.xmlFiles):
            try:
                fbn = op.basename(f)
                df = self.convertXMLtoDataframe(f)
                dfs.append(df)
                print(f'[INFO] [INNER] [{i+1}/{len(self.xmlFiles)}] File <{fbn}> has been successfully converted :D')
            except Exception as e:
                print(f'[ERR] [{i+1}/{len(self.xmlFiles)}] Failed in converting file <{fbn}> :( >>>>{e}<<<<')
        print(f'[INFO] Merging ... ', end='')
        dfs = pd.concat(dfs, sort=False)
        dfs['Received'] = dfs['Received'].apply(lambda x: dateutil.parser.parse(x).strftime('%Y-%m-%d %H:%M:%S'))
        print('Finished')
        print(f'[INFO] Writing to excel file ... ', end='')
        outFileName = op.join(self.outPath, op.basename(self.dirPath) + self.excelExt)
        dfs.to_excel(outFileName, index=False)
        print('Finished')
        print(f'[INFO] Output excel file: <{outFileName}>')
        print(f'[INFO] ===== DONE! <{self.dirbn}> =====\n')


class MainFrame(XMLtoExcelWindow):
    def __init__(self, parent):
        XMLtoExcelWindow.__init__(self, parent, title=u"XML 文件批量转换助手 v1.3.0")

        folderDrag = FileDropTarget(self.textFolderInput)
        self.textFolderInput.SetDropTarget(folderDrag)

        pathDrag = FileDropTarget(self.textOutPath)
        self.textOutPath.SetDropTarget(pathDrag)

        redir = RedirectText(self.logOutput)
        sys.stdout = redir

    def clearText(self, event):
        self.textFolderInput.SetValue('')

    def OnAbout(self, event):
        wx.MessageBox('Copyright © 2018 安安 lbgnmic@gmail.com',
                      '关于本程序',
                      wx.OK | wx.ICON_INFORMATION)

    def OnHelp(self, event):
        wx.MessageBox('1. 输入待处理的包含 XML 文件的目录，或拖放置左侧输入框中，点击「执行」开始合并\n'
                      '2. 合并的 Excel 文件将输出至「Excel 文件输出目录」，若置空，则输出目录与输入目录相同\n'
                      '3. 每个目录中所有的 XML 文件输出成一个 Excel 文件', '使用说明', wx.ICON_INFORMATION | wx.OK)

    def mergeFiles(self, event):
        inList = self.textFolderInput.GetValue().strip().splitlines()
        dirList = []
        for l in inList:
            if l and l not in dirList:
                dirList.append(l)

        if self.textOutPath.GetValue():
            excelPath = self.textOutPath.GetValue()
        else:
            excelPath = None

        def runner():
            self.runButton.Enabled = False
            for l in dirList:
                XMLtoExcel(l, outPath=excelPath).convertAllXMLtoExcel()
            self.runButton.Enabled = True
            if dirList:
                print(f'[INFO] =================== FINISHED ===================')

        t = Thread(target=runner)
        t.daemon = True
        t.start()


def importMan():
    global pd
    import importlib
    pd = importlib.import_module('pandas')


if __name__ == '__main__':
    app = wx.App(False)
    frame = MainFrame(None)
    frame.Show(True)

    t = Thread(target=importMan)
    t.daemon = True
    t.start()

    app.MainLoop()
